package info.emamian.droplet.data;

import java.util.ArrayList;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
import com.amazonaws.services.dynamodbv2.model.CreateTableResult;
import com.amazonaws.services.dynamodbv2.model.DescribeTableRequest;
import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
import com.amazonaws.services.dynamodbv2.model.KeyType;
import com.amazonaws.services.dynamodbv2.model.ListTablesResult;
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
import com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException;
import com.amazonaws.services.dynamodbv2.model.TableDescription;
import com.amazonaws.services.dynamodbv2.model.TableStatus;

@Component
public class DynamoDbInitialize implements ApplicationListener<ContextRefreshedEvent> {
	private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbInitialize.class);

	AmazonDynamoDBClient client;

	@Override
	public void onApplicationEvent(final ContextRefreshedEvent arg0) {
		LOGGER.info("DynamoDbInitialize started.");
		client = DynamoDbClientFactory.getClientInstance();
		ListTablesResult tables = client.listTables();
		LOGGER.info("tables: ");
		for (String tb : tables.getTableNames()) {
			getTableInformation(tb);
		}

		// DeleteTableRequest deleteTableRequest = new
		// DeleteTableRequest().withTableName(DynamoDbSms.TABLE_NAME);
		// DeleteTableResult result = client.deleteTable(deleteTableRequest);

		if (!tables.getTableNames().contains(DynamoDbSms.TABLE_NAME)) {
			LOGGER.info("Table {} does not exist, creating...", DynamoDbSms.TABLE_NAME);
			createTableWithAutogeneratedIdHashKey(DynamoDbSms.TABLE_NAME, 325L, 325L);
		}
		if (!tables.getTableNames().contains(SystemEventsDynamoDB.TABLE_NAME)) {
			LOGGER.info("Table {} does not exist, creating...", SystemEventsDynamoDB.TABLE_NAME);
			createTableWithAutogeneratedIdHashKey(SystemEventsDynamoDB.TABLE_NAME, 5L, 5L);
		}
	}

	private void createTableWithAutogeneratedIdHashKey(final String tableName, final long provisionedRead, final long provisionedWrite) {
		ArrayList<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>();
		attributeDefinitions.add(new AttributeDefinition().withAttributeName("Id").withAttributeType("S"));

		ArrayList<KeySchemaElement> ks = new ArrayList<KeySchemaElement>();
		ks.add(new KeySchemaElement().withAttributeName("Id").withKeyType(KeyType.HASH));

		ProvisionedThroughput provisionedThroughput = new ProvisionedThroughput().withReadCapacityUnits(provisionedRead).withWriteCapacityUnits(provisionedWrite);

		CreateTableRequest request = new CreateTableRequest().withTableName(tableName).withAttributeDefinitions(attributeDefinitions).withKeySchema(ks)
				.withProvisionedThroughput(provisionedThroughput);

		CreateTableResult result = client.createTable(request);
		LOGGER.info("Table creation result:{}", result);
		waitForTableToBecomeAvailable(tableName);
		getTableInformation(tableName);
	}

	private void getTableInformation(final String tableName) {

		TableDescription tableDescription = client.describeTable(new DescribeTableRequest().withTableName(tableName)).getTable();
		LOGGER.info(String.format(
				"\n=========================\nName: %s:\n" + "Status: %s \n" + "Provisioned Throughput (read capacity units/sec): %d \n"
						+ "Provisioned Throughput (write capacity units/sec): %d \n" + " Attribute Definitions: %s\n" + " Key Schema: %s\n" + "Item count:"
						+ tableDescription.getItemCount() + "\n==========================\n", tableDescription.getTableName(), tableDescription.getTableStatus(), tableDescription
						.getProvisionedThroughput().getReadCapacityUnits(), tableDescription.getProvisionedThroughput().getWriteCapacityUnits(), tableDescription
						.getAttributeDefinitions(), tableDescription.getKeySchema()));
	}

	private void waitForTableToBecomeAvailable(final String tableName) {
		LOGGER.info("Waiting for " + tableName + " to become ACTIVE...");

		long startTime = System.currentTimeMillis();
		long endTime = startTime + (10 * 60 * 1000);
		while (System.currentTimeMillis() < endTime) {
			DescribeTableRequest request = new DescribeTableRequest().withTableName(tableName);
			TableDescription tableDescription = client.describeTable(request).getTable();
			String tableStatus = tableDescription.getTableStatus();
			LOGGER.info("  - current state: " + tableStatus);
			if (tableStatus.equals(TableStatus.ACTIVE.toString())) { return; }
			try {
				Thread.sleep(1000 * 20);
			}
			catch (Exception e) {
			}
		}
		throw new RuntimeException("Table " + tableName + " never went active");
	}

	@SuppressWarnings("unused")
	private void waitForTableToBeDeleted(final String tableName) {
		LOGGER.info("Waiting for " + tableName + " while status DELETING...");

		long startTime = System.currentTimeMillis();
		long endTime = startTime + (10 * 60 * 1000);
		while (System.currentTimeMillis() < endTime) {
			try {
				DescribeTableRequest request = new DescribeTableRequest().withTableName(tableName);
				TableDescription tableDescription = client.describeTable(request).getTable();
				String tableStatus = tableDescription.getTableStatus();
				LOGGER.info("  - current state: " + tableStatus);
				if (tableStatus.equals(TableStatus.ACTIVE.toString())) { return; }
			}
			catch (ResourceNotFoundException e) {
				LOGGER.info("Table " + tableName + " is not found. It was deleted.");
				return;
			}
			try {
				Thread.sleep(1000 * 20);
			}
			catch (Exception e) {
			}
		}
		throw new RuntimeException("Table " + tableName + " was never deleted");
	}

}
